as.run(['$templateCache', function ($templateCache) {
    var head = [], base = [], list = [], page = [], tree = [];

    head.push('<thead>');
    head.push('  <tr>');
    head.push('    <th ng-if="::$ctrl.checkbox" class="grid-checkbox">');
    head.push('      <a-checkbox ng-model="$ctrl.selectAll" ng-change="$ctrl.selChange()"></a-checkbox>');
    head.push('    </th>');
    head.push('    <th ng-repeat="col in ::$ctrl.column" ng-class="::{index:col.index, action: !!col.items}" ng-style="::{width:col.$width}">');
    head.push('      <span ng-bind="::col.text"></span>');
    head.push('    </th>');
    head.push('  </tr>');
    head.push('</thead>');

    base.push('<tbody>');
    base.push('  <tr ng-repeat="d in $ctrl.data track by $index" ng-init="__rowIndex = $index; __first = $first" ng-class="{active:$ctrl.allChecks[$index]}">');
    base.push('    <td ng-if="::$ctrl.checkbox" class="grid-checkbox">');
    base.push('      <a-checkbox checked="$ctrl.allChecks[$index]"></a-checkbox>');
    base.push('    </td>');
    base.push('    <td ng-repeat="col in $ctrl.column track by $index" ng-class="::{index: col.index, action: !!col.items}">');
    base.push('      <span ng-if="!col.renderer && !col.items" ng-bind="col.index==true?(__rowIndex+1):(d|convert:col)"></span>');
    base.push('      <span ng-if="col.renderer" ng-bind="col.renderer(d)"></span>');
    base.push('      <i ng-if="col.items" ng-repeat="item in col.items track by $index" class="{{item.icon}} glyphicon" a-tooltip="{{item.tooltip}}" tooltip-placement="{{__first?\'bottom\':\'top\'}}" ng-click="item.handler($ctrl.grid, d, __rowIndex)" ng-class="{disabled:item.disable(d)}"></i>');
    base.push('    </td>');
    base.push('  </tr>');
    base.push('</tbody>');

    tree.push('<tbody>');
    tree.push('  <tr ng-repeat="row in $ctrl.treeRows | visibleFilter track by $index" ng-init="__rowIndex = $index; __first = $first" class="level-{{row.level}}">');
    //tree.push('    <td ng-if="$ctrl.option.checkbox">');
    //tree.push('      <a-checkbox checked="$ctrl.allChecks[$index]"></a-checkbox>');
    //tree.push('    </td>');
    tree.push('    <td ng-repeat="col in $ctrl.option.column track by $index" ng-class="{action: !!col.items}">');
    tree.push('      <a ng-if="col.tree">');
    tree.push('        <i ng-class="row.tree_icon" ng-click="!row.item.isLeaf && (row.item.expanded = !row.item.expanded);" class="indented glyphicon link"></i>');
    tree.push('      </a>');
    tree.push('      <span ng-if="!col.renderer && !col.items" ng-bind="col.index==true?(__rowIndex+1):(row.item|convert:col)" ng-class="{indented:col.tree}"></span>');
    tree.push('      <span ng-if="col.renderer" ng-bind="col.renderer(row.item)" ng-class="{indented:col.tree}"></span>');
    tree.push('      <i ng-if="col.items" ng-repeat="item in col.items track by $index" class="{{item.icon}} glyphicon" a-tooltip="{{item.tooltip}}" tooltip-placement="{{__first?\'bottom\':\'top\'}}" ng-click="!item.disable(row.item) && item.handler(this, row.item, __rowIndex)" ng-class="{indented:col.tree, disabled:item.disable(row.item)}"></i>');
    tree.push('    </td>');
    tree.push('  </tr>');
    tree.push('</tbody>');

    var getGrid = function (tbody) {
        var content = [];
        content.push('<div class="a-grid">');
        content.push('  <div class="fixed-grid-head" ng-class="{\'has-scroll\':$ctrl.$hasScroll}">');
        content.push('    <div class="scroll-blank" ng-if="$ctrl.$hasScroll" ng-style="$ctrl.$scrollStyle"></div>');
        content.push('    <div class="head">');
        content.push('      <table class="ui table celled small">');
        content.push(head.join(''));
        content.push('      </table>');
        content.push('    </div>');
        content.push('  </div>');
        content.push('  <div class="fixed-grid-body" ng-switch="$ctrl.$loadState">');
        content.push('    <table class="ui table celled small selectable compact" ng-switch-when="finished">');
        content.push(head.join(''));
        content.push(tbody);
        content.push('    </table>');
        content.push('    <div class="ui active inverted dimmer" ng-switch-when="loading">');
        content.push('      <div class="ui loader"></div>');
        content.push('    </div>');
        content.push('    <div class="no-data" ng-switch-default>');
        content.push('      <h6 class="text-center text-muted">');
        content.push('        <i class="glyphicon glyphicon-info-sign"></i>');
        content.push('        暂无数据');
        content.push('      </h6>');
        content.push('    </div>');
        content.push('  </div>');
        content.push('  <div ng-transclude="" class="fixed-grid-footer"></div>');
        content.push('</div>');
        return content.join('');
    };

    list.push('<a-base-grid checkbox="$ctrl.option.checkbox" data="$ctrl.data" column="$ctrl.option.column" loading="$ctrl.loading" name="$ctrl.grid" class="a-base-grid">');
    list.push('  <a-store api="{{$ctrl.option.api}}" params="$ctrl.params" ng-model="$ctrl.storeData" ng-change="$ctrl.dataChange()" state="$ctrl.loadState"></a-store>');
    list.push('</a-base-grid>');

    page.push('<a-base-grid checkbox="$ctrl.option.checkbox" data="$ctrl.data" column="$ctrl.option.column" loading="$ctrl.loading" name="$ctrl.grid" class="a-base-grid">');
    page.push('  <a-store api="{{$ctrl.option.api}}" params="$ctrl.$param" ng-model="$ctrl.storeData" ng-change="$ctrl.dataChange()" state="$ctrl.loadState"></a-store>');
    page.push('  <a-pagination ng-if="::$ctrl.count===false" ng-model="$ctrl.pageNum" ng-change="$ctrl.pageChange()" last="$ctrl.last" first-text="第一页"></a-pagination>');
    page.push('  <a-pagination ng-if="::$ctrl.count!==false" ng-model="$ctrl.pageNum" ng-change="$ctrl.pageChange()" total-page="$ctrl.totalPage" last="$ctrl.last" first-text="第一页" last-text="最后页"></a-pagination>');
    page.push('</a-base-grid>');

    $templateCache.put('grid/base.html', getGrid(base.join('')));
    $templateCache.put('grid/list.html', list.join(''));
    $templateCache.put('grid/page.html', page.join(''));
    $templateCache.put('grid/tree.html', getGrid(tree.join('')));
}]).filter('convert', ['$filter', function ($filter) {
    return function (input, opt) {
        var text = input ? input[opt.name] : '',
            type = opt.type || 'string';
        if (type === 'string') {
            return text;
        }
        if (type === 'date' && !opt.format) {
            text = $filter('date')(text, 'yyyy-MM-dd');
        }
        if (type === 'datetime' && !opt.format) {
            text = $filter('date')(text, 'yyyy-MM-dd HH:mm:ss');
        }
        if (opt.format) {
            text = $filter(type)(text, opt.format);
        }
        return text;
    };
}]).filter('visibleFilter', function () {
    return function (arr) {
        if (!arr) return;
        var filtered = [];
        for (var i = 0; i < arr.length; i++) {
            var item = arr[i];
            item.visible && filtered.push(item);
        }
        return filtered;
    };
}).controller('$$BaseGridCtrl', ['$scope', '$element', '$timeout', '$window', function ($scope, $element, $timeout, $window) {
    var ctrl = this, totalRatio, el = $element[0], dataLength, head = el.querySelector('.fixed-grid-head'), body = el.querySelector('.fixed-grid-body');
    ctrl.allChecks = [];
    ctrl.grid = ctrl.grid || {};
    totalRatio = ctrl.column.reduce(function (prev, curr) {
        var ratio = curr.index ? 0 : (+curr.ratio || 1);
        return prev + ratio;
    }, 0);
    angular.forEach(ctrl.column, function (col) {
        if (col.index) {
            col.$width = false;
        } else {
            var ratio = +col.ratio || 1;
            col.$width = ratio * 100 / totalRatio + '%';
        }
    });
    var layout = function () {
        ctrl.$hasScroll = body.clientHeight < body.scrollHeight;
        if (ctrl.$hasScroll = body.clientHeight < body.scrollHeight) {
            ctrl.$scrollStyle = {
                width : (body.parentNode.clientWidth - body.clientWidth) + 'px',
                height: head.clientHeight + 'px'
            };
        }
    };
    var genRowSelectState = function (flag) {
        var all = dataLength;
        ctrl.allChecks.length = 0;
        while (all--) {
            ctrl.allChecks.push(flag);
        }
    };
    var reset = function () {
        ctrl.data = ctrl.data || [];
        dataLength = ctrl.data.length;
        ctrl.selectAll = false;
        genRowSelectState(false);
    };
    var dataChange = function () {
        reset();
        $timeout(function () {
            layout();
        });
    };
    $window.onresize = function () {
        $timeout(function () {
            layout();
        });
    };
    ctrl.selChange = function () {
        genRowSelectState(ctrl.selectAll);
    };
    $scope.$watchCollection('$ctrl.data', function () {
        dataChange();
    });
    $scope.$watch('$ctrl.loading', function (loading) {
        if (loading) {
            ctrl.$loadState = 'loading';
        } else {
            ctrl.data.length === 0 ? (ctrl.$loadState = 'noData') : (ctrl.$loadState = 'finished');
        }
    });

    /////////////////////////////////////////////////////////
    ctrl.grid.getSelectRow = function () {
        var rows = ctrl.data.filter(function (curr, index) {
            return ctrl.allChecks[index];
        });
        return angular.copy(rows);
    };
}]).controller('$$ListGridCtrl', ['$scope', function ($scope) {
    var ctrl = this;
    ctrl.grid = ctrl.grid || {};
    ctrl.dataChange = function () {
        if (!ctrl.storeData) {
            ctrl.data = [];
            return;
        }
        var data = ctrl.storeData.data;
        if (data && ctrl.option.dataIndex) {
            data = data[ctrl.option.dataIndex];
        }
        ctrl.data = data;
    };
    $scope.$watch('$ctrl.loadState', function () {
        ctrl.loading = ctrl.loadState === 1;
    });

    /////////////////////////////////////////////////////////
    ctrl.grid.reload = function () {
        ctrl.params._dc = new Date().getTime();
    };
}]).controller('$$PageGridCtrl', ['$scope', function ($scope) {
    var ctrl = this, PAGE_START = 1, PAGE_SIZE = 25, pageSize = ctrl.pageSize || PAGE_SIZE, count = ctrl.count !== false;
    ctrl.grid = ctrl.grid || {};
    ctrl.pageNum = PAGE_START;
    ctrl.option.dataIndex = ctrl.option.dataIndex || 'content';
    if (!count) {
        pageSize = -(PAGE_SIZE + 1);
    }
    var reset = function () {
        ctrl.pageNum = PAGE_START;
    };
    var genSearchParam = function () {
        ctrl.$param = angular.extend({}, ctrl.params, {
            pageNum : ctrl.pageNum,
            pageSize: pageSize
        });
    };
    ctrl.pageChange = function () {
        genSearchParam();
    };
    ctrl.dataChange = function () {
        if (!ctrl.storeData) {
            ctrl.data = [];
            return;
        }
        var data = ctrl.storeData.data;
        if (data && ctrl.option.dataIndex) {
            data = data[ctrl.option.dataIndex];
        }
        if (count) {
            ctrl.last = ctrl.storeData.data.last;
            ctrl.totalPage = ctrl.storeData.data.totalPages;
        } else {
            !(ctrl.last = !(data.length > PAGE_SIZE)) && data.pop();
        }
        ctrl.data = data;
    };
    $scope.$watch('$ctrl.loadState', function () {
        ctrl.loading = ctrl.loadState === 1;
    });
    $scope.$watch('$ctrl.params', function () {
        reset();
        genSearchParam();
    }, true);

    /////////////////////////////////////////////////////////
    ctrl.grid.reload = function () {
        ctrl.params._dc = new Date().getTime();
    };
}]).controller('$$TreeGridCtrl', ['$http', '$scope', '$timeout', '$element', function ($http, $scope, $timeout, $element) {
    var ctrl = this, el = $element[0], head = el.querySelector('.fixed-grid-head'), body = el.querySelector('.fixed-grid-body');
    ctrl.treeRows = [];
    ctrl.tree = ctrl.tree || {};
    ctrl.column = ctrl.option.column;
    var childIndex    = ctrl.option.childIndex || 'children',
        hasDataIndex  = !!ctrl.option.dataIndex,
        hasLeafFn     = angular.isFunction(ctrl.option.isLeaf),// 用户是否提供了判断是否叶子节点的方法
        iconLeaf      = ctrl.option.iconLeaf || 'glyphicon-file',
        iconCollapse  = ctrl.option.iconCollapse || 'glyphicon-chevron-down',
        iconExpand    = ctrl.option.iconExpand || 'glyphicon-chevron-right',
        firstLoadData = false;

    var initData = function () {
        ctrl.$loadState = 'loading';
        $http.get(ctrl.option.api).then(function (result) {
            ctrl.rowData = hasDataIndex ? result.data[ctrl.option.dataIndex] : result.data;
            ctrl.$loadState = ctrl.rowData.length ? 'finished' : 'default';
            if (!firstLoadData) {
                firstLoadData = true;
                $scope.$watch('$ctrl.rowData', RowDataWatcher, true);
            }
        }, function () {
            ctrl.$loadState = 'error';
        });
    };
    var layout = function () {
        ctrl.$hasScroll = body.clientHeight < body.scrollHeight;
        if (ctrl.$hasScroll = body.clientHeight < body.scrollHeight) {
            ctrl.$scrollStyle = {
                width : (body.parentNode.clientWidth - body.clientWidth) + 'px',
                height: head.clientHeight + 'px'
            };
        }
    };
    var RowDataWatcher = function () {
        var _ref = ctrl.rowData, _i, _len;
        if (!_ref) return;
        ctrl.treeRows = [];
        function convertData(level, item, visible) {
            var tree_icon, _i, child, child_visible, _len, _ref;
            if (!item.expanded) {
                item.expanded = false;
            }
            if ((hasLeafFn && ctrl.option.isLeaf(item)) || item.isLeaf) {
                tree_icon = iconLeaf;
            } else {
                tree_icon = item.expanded ? iconCollapse : iconExpand;
            }
            item.level = level;
            ctrl.treeRows.push({
                level    : level,
                item     : item,
                tree_icon: tree_icon,
                visible  : visible
            });
            if (item[childIndex] != null) {
                _ref = item[childIndex];
                for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                    child = _ref[_i];
                    child_visible = visible && item.expanded;
                    convertData(level + 1, child, child_visible);
                }
            }
        }

        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            convertData(1, _ref[_i], true);
        }
        $timeout(function () {
            layout();
        });
    };
    initData();

    /////////////////////////////////////////////////////////
    ctrl.tree.reload = function () {
        initData();
    };
    ctrl.tree.getTreeData = function () {
        return angular.copy(ctrl.treeRows);
    };
    ctrl.tree.setTreeData = function (data) {
        return ctrl.rowData = data;
    };
}]).component('aBaseGrid', {
    bindings   : {
        grid    : '=name',
        data    : '<', // [object Array]
        checkbox: '<', // [object Boolean]
        column  : '<', // [object Array]
        loading : '<'  // [object Boolean]
    },
    transclude : true,
    templateUrl: 'grid/base.html',
    controller : '$$BaseGridCtrl'
}).component('aListGrid', {
    bindings   : {
        option: '<',
        grid  : '=?name',
        params: '<?'
    },
    templateUrl: 'grid/list.html',
    controller : '$$ListGridCtrl'
}).component('aPageGrid', {
    bindings   : {
        option: '<',
        grid  : '=?name',
        params: '<?',
        count : '<' // 是否查询总数
    },
    templateUrl: 'grid/page.html',
    controller : '$$PageGridCtrl'
}).component('aTreeGrid', {
    bindings   : {
        option: '<',
        tree  : '=name'
    },
    transclude : true,
    templateUrl: 'grid/tree.html',
    controller : '$$TreeGridCtrl'
});